package teamproject.visualization.gui.behaviors;

import java.awt.AWTEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.media.j3d.Behavior;
import javax.media.j3d.Bounds;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnElapsedFrames;
import javax.media.j3d.WakeupOr;
import javax.vecmath.Vector3d;

import teamproject.graphEditor.Controller;
import teamproject.graphEditor.PDEEdge;
import teamproject.graphEditor.SortCollectionsToArray;
import teamproject.visualization.calculations.VectorCalc;
import teamproject.visualization.gui.SpaceBarProcessor;
import teamproject.visualization.gui.Visualizer;

/**
 * Schedules which packets have to be started sending on an edge.
 */
public class PackageEdgeScheduler extends Behavior implements Resettable {

	private ArrayList<PackageEdgeBehavior> edgeBehaviors;
	private SpaceBarProcessor sbp;
	private SortCollectionsToArray scta = new SortCollectionsToArray();
	private PDEEdge edge;
	private PackageEdgeBehavior previous = null;

	private boolean started = false; // has the first packet started moving

	private int upperTimeBound;
	private int rowCounter = 0;
	private int packetCounter = 0;
	private int framesCounted = 0;
	private float dataValue;
	private Vector3d initialTranslate = new Vector3d();
	private float newoffset;

	private WakeupCriterion[] warray;
	private WakeupOr criterion;
	private WakeupOnAWTEvent wakeupevent;
	private WakeupCriterion wakeupcriterion;
	private AWTEvent[] events;

	private Controller control;

	public PackageEdgeScheduler(ArrayList<PackageEdgeBehavior> edgeBehaviors,
			PDEEdge edge, int upperTimeBound, Bounds bounds, Controller control) {
		this.edgeBehaviors = edgeBehaviors;
		this.edge = edge;
		this.upperTimeBound = upperTimeBound;
		this.control = control;
		this.sbp = new SpaceBarProcessor();
		setSchedulingBounds(bounds);
		scta.sortPackageEdgeBehaviors(this.edgeBehaviors);
	}

	private void doProcess() {
		if (!sbp.hasStopped()) {

			if (rowCounter < upperTimeBound) {
				if (/* !hold */(framesCounted % Visualizer.FRAMECOUNT) == 0) {
					dataValue = edge.getCellAttributes().get(rowCounter);
					rowCounter++;
				}

				if (started) {
					if (dataValue > 0.0) {
						float distance = VectorCalc.calculateLength(
								VectorCalc.getSource(control, edge),
								previous.getCurrentPacketPosition());
						if (distance > (Visualizer.C / dataValue)) {
							// start next packet
							packetCounter++;
							if (packetCounter < edgeBehaviors.size()) {
								edgeBehaviors.get(packetCounter).setStarted(
										true);
								previous = edgeBehaviors.get(packetCounter);
							}
						}
					}
				}

				if (dataValue > 0.0 && !started) {
					// start first packet
					started = true;
					previous = edgeBehaviors.get(0);
					edgeBehaviors.get(0).setStarted(true);
				}
				framesCounted++;
				for (int i = 0; i < edgeBehaviors.size(); i++) {
					if (edgeBehaviors.get(i).hasStarted()) {
						edgeBehaviors.get(i).doProcess();
					}
				}
			}
		}
	}

	private Vector3d getInitialTranslate(int packetCount) {
		newoffset = Visualizer.OFFSET * packetCount;

		// nur X-Richtung benutzen. So zeigen die Paket in die richtige
		// Richtung.

		initialTranslate.x = newoffset;
		initialTranslate.y = 0;
		initialTranslate.z = 0;

		return initialTranslate;
	}

	@Override
	public void initialize() {
		warray = new WakeupCriterion[2];

		warray[0] = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
		warray[1] = new WakeupOnElapsedFrames(Visualizer.SPEEDFACTOR);
		criterion = new WakeupOr(warray);

		wakeupOn(criterion);
	}

	@Override
	public void processStimulus(Enumeration criteria) {

		while (criteria.hasMoreElements()) {
			wakeupcriterion = (WakeupCriterion) criteria.nextElement();
			if (wakeupcriterion instanceof WakeupOnAWTEvent) {
				wakeupevent = (WakeupOnAWTEvent) wakeupcriterion;
				events = wakeupevent.getAWTEvent();
				sbp.processAWTEvents(events);
			} else if (wakeupcriterion instanceof WakeupOnElapsedFrames) {
				doProcess();
			}
		}

		wakeupOn(criterion);
	}

	@Override
	public void reset() {
		started = false;

		rowCounter = 0;
		packetCounter = 0;
		framesCounted = 0;

		previous = null;
	}
}
